/**
 ******************************************************************************
 * @file    main.c
 * @author  fire
 * @version V1.0
 * @date    2024-xx-xx
 * @brief   OLED
 ******************************************************************************
 * @attention
 *
 * 实验平台:野火 F103 STM32 开发板
 * 论坛    :http://www.firebbs.cn
 * 淘宝    :https://fire-stm32.taobao.com
 *
 ******************************************************************************
 */
#include "bsp_oled_debug.h"
#include "../../Core/Inc/gpio.h"
#include "bsp_oled_codetab.h"
// #include "iic/bsp_iic_debug.h"
// extern I2C_HandleTypeDef iic_initstruct;
//=====================I2C_START======================//
void oled_sda_pin_output_high(void);
void oled_sda_pin_output_low(void);
void oled_scl_pin_output_high();
void oled_scl_pin_output_low();

void oled_delay_us(int us)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < us; i++)
    {
        for (j = 0; j <8; j++)
        {
            __NOP();
        }
    }
}
void oled_i2c_test(void)
{
    oled_sda_pin_output_high();
    oled_scl_pin_output_high();
    oled_delay_us(1);
    oled_sda_pin_output_low();
    oled_scl_pin_output_low();
    oled_delay_us(1);
}
void oled_init_i2c_sensor_port(void)
{
    //
}
#define OLED_DSA_PIN   GPIO_PIN_9
#define OLED_DSA_PORT  GPIOB
#define OLED_SCL_PIN 		GPIO_PIN_8
#define OLED_SCL_PORT  GPIOB
void oled_sda_pin_output_high(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
     GPIO_InitStruct.Pin              = OLED_DSA_PIN;
    GPIO_InitStruct.Mode             = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull             = GPIO_NOPULL;
    GPIO_InitStruct.Speed            = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_DSA_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(OLED_DSA_PORT, OLED_DSA_PIN, GPIO_PIN_SET);
}

void oled_sda_pin_output_low(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin              = OLED_DSA_PIN;
    GPIO_InitStruct.Mode             = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull             = GPIO_NOPULL;
    GPIO_InitStruct.Speed            = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_DSA_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(OLED_DSA_PORT, OLED_DSA_PIN, GPIO_PIN_RESET);
}
void oled_sda_pin_input(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin              = OLED_DSA_PIN;
    GPIO_InitStruct.Mode             = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull             = GPIO_NOPULL;
    HAL_GPIO_Init(OLED_DSA_PORT, &GPIO_InitStruct);
}
uint8_t oled_sda_pin_read_level()
{
    int     CNT   = 0;
    uint8_t level = 0;
    do
    {
        level = HAL_GPIO_ReadPin(OLED_DSA_PORT, OLED_DSA_PIN);
        CNT++;
        oled_delay_us(1);
    } while (level == 1 && CNT < 5);
    return level;
}
void oled_scl_pin_output_high(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin              = OLED_SCL_PIN;
    GPIO_InitStruct.Mode             = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull             = GPIO_NOPULL;
    GPIO_InitStruct.Speed            = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_SCL_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(OLED_SCL_PORT, OLED_SCL_PIN, GPIO_PIN_SET);
}

void oled_scl_pin_output_low(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin              = OLED_SCL_PIN;
    GPIO_InitStruct.Mode             = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull             = GPIO_NOPULL;
    GPIO_InitStruct.Speed            = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_SCL_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(OLED_SCL_PORT, OLED_SCL_PIN, GPIO_PIN_RESET);
}

void oled_i2c_start(void)
{
    oled_sda_pin_output_high();
    oled_delay_us(8);
    oled_scl_pin_output_high();
    oled_delay_us(8);
    oled_sda_pin_output_low();
    oled_delay_us(8);
    oled_scl_pin_output_low();
    oled_delay_us(8);
}

void oled_i2c_write_byte(uint8_t Byte)
{
    uint8_t Data, N, i;
    Data = Byte;
    i    = 0x80;
    for (N = 0; N < 8; N++)
    {
        oled_scl_pin_output_low();
        oled_delay_us(4);
        if (i & Data)
        {
            oled_sda_pin_output_high();
        }
        else
        {
            oled_sda_pin_output_low();
        }

        oled_scl_pin_output_high();
        oled_delay_us(4);
        Data <<= 1;
    }
    oled_scl_pin_output_low();
    oled_delay_us(8);
    oled_sda_pin_input();
    oled_delay_us(8);
}

uint8_t oled_i2c_read_byte(void)
{
    uint8_t Byte, i, a;
    Byte = 0;
    oled_scl_pin_output_low();
    oled_sda_pin_input();
    oled_delay_us(8);
    for (i = 0; i < 8; i++)
    {
        oled_scl_pin_output_high();
        oled_delay_us(5);
        if (oled_sda_pin_read_level() == 1)
        {
            a = 1;
        }
        else
        {
            a = 0;
        }
        Byte = (Byte << 1) | a;
        oled_scl_pin_output_low();
        oled_delay_us(5);
    }
    oled_sda_pin_input();
    oled_delay_us(8);
    return Byte;
}

uint8_t oled_i2c_wait_ack(void)
{
    uint8_t read = 0;
    oled_scl_pin_output_low();
    oled_sda_pin_input();
    oled_delay_us(8);
    oled_scl_pin_output_high();
    oled_delay_us(8);
    read = oled_sda_pin_read_level();
    oled_scl_pin_output_low();
    oled_delay_us(8);
    return read;
}

void oled_i2c_send_ack(void)
{
    oled_scl_pin_output_low();
    oled_delay_us(8);
    oled_sda_pin_output_low();
    oled_delay_us(8);
    oled_scl_pin_output_high();
    oled_delay_us(8);
    oled_scl_pin_output_low();
    oled_delay_us(8);
    oled_sda_pin_input();
    oled_delay_us(8);
}

void oled_i2c_send_Nack(void)
{
    oled_scl_pin_output_low();
    oled_delay_us(8);
    oled_sda_pin_output_high();
    oled_delay_us(8);
    oled_scl_pin_output_high();
    oled_delay_us(8);
    oled_scl_pin_output_low();
    oled_delay_us(8);
    oled_sda_pin_output_low();
    oled_delay_us(8);
}

void oled_i2_stop(void)
{
    oled_sda_pin_output_low();
    oled_delay_us(8);
    oled_scl_pin_output_high();
    oled_delay_us(8);
    oled_sda_pin_output_high();
    oled_delay_us(8);
}
//=====================I2C_END======================//
/* oled写数据 */
void Oled_Write_Data(uint8_t data)
{
#if OLED_IIC_HARDWARE
    HAL_I2C_Mem_Write(&iic_initstruct, OLED_ID, OLED_WR_DATA, I2C_MEMADD_SIZE_8BIT, &data, 1, 0x100);
#else
    oled_i2c_start();
    oled_i2c_write_byte(OLED_ID);
    /* 等待应答 */
   oled_i2c_wait_ack();
    oled_i2c_write_byte(OLED_WR_DATA);
    /* 等待应答 */
    oled_i2c_wait_ack();
    oled_i2c_write_byte(data);
    /* 等待应答 */
   oled_i2c_wait_ack();
    oled_i2_stop();
#endif
}

/* oled写命令 */
void Oled_Write_Cmd(uint8_t cmd)
{
#if OLED_IIC_HARDWARE
    HAL_I2C_Mem_Write(&iic_initstruct, OLED_ID, OLED_WR_CMD, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 0x100);
#else
    oled_i2c_start();
    oled_i2c_write_byte(OLED_ID);
    /* 等待应答 */
    oled_i2c_wait_ack();
    oled_i2c_write_byte(OLED_WR_CMD);
    /* 等待应答 */
    oled_i2c_wait_ack();
    oled_i2c_write_byte(cmd);
    /* 等待应答 */
    oled_i2c_wait_ack();
    oled_i2_stop();
#endif
}

void OLED_Init(void)
{
    /* 设置显示打开/关闭
     * AE--->显示打开
     * AF--->显示关闭(休眠模式)
     */
    Oled_Write_Cmd(0xAE);

    /* ================== 基本命令表 ===================*/
    /* 设置对比度
     * 0~255：数值越大 亮度越亮
     */
    Oled_Write_Cmd(0x81);
    Oled_Write_Cmd(0xFF);

    /* 使能全屏显示
     * A4--->恢复到RAM内容显示
     * A5--->忽略RAM内容显示
     */
    Oled_Write_Cmd(0xA4);

    /* 设置显示模式
     * A6--->正常显示：0灭1亮
     * A7--->逆显示：1灭0亮
     */
    Oled_Write_Cmd(0xA6);

    /* ================== 滚动命令表 ===================*/
    /* 滚动使能/失能
     * 2E--->失能
     * 2F--->使能
     */
    Oled_Write_Cmd(0x2E);

    /* 七字节命令： 连续水平滚动设置 */

    /* 左/右水平滚动设置
     * 26--->右水平滚动
     * 27--->左水平滚动
     */
    Oled_Write_Cmd(0x26);
    /* 虚拟字节 */
    Oled_Write_Cmd(0x00);
    /* 设置滚动起始页地址 */
    Oled_Write_Cmd(0x00);
    /* 设置滚动间隔 */
    Oled_Write_Cmd(0x03);
    /* 设置滚动结束地址 */
    Oled_Write_Cmd(0x07);
    /* 虚拟字节 */
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0xFF);

    /* =============== 寻址设置命令表 ==================*/

    /* 双字节命令:寄存器寻址模式 */
    Oled_Write_Cmd(0x20);

    /* 10:页寻址模式
     * 01:垂直寻址模式
     * 00:水平寻址模式
     */
    Oled_Write_Cmd(0x10);
    /* 单字节命令:设置页寻址的起始页地址 */
    Oled_Write_Cmd(0xB0);
    /* 单字节命令:设置页寻址的起始列地址低位 */
    Oled_Write_Cmd(0x00);
    /* 单字节命令:设置页寻址的起始列地址高位 */
    Oled_Write_Cmd(0x10);

    /*=============== 硬件配置命令表 ==================*/

    /* 设置显示开始线
     * 0x40~0x7F对应0~63
     */
    Oled_Write_Cmd(0x40);

    /* 设置列重映射
     * A0:addressX--->segX
     * A1:addressX--->seg(127-X)
     */
    Oled_Write_Cmd(0xA1);

    /* 设置多路复用比 */
    Oled_Write_Cmd(0xA8);
    Oled_Write_Cmd(0x3F);

    /* 设置COM输出扫描方向
     * C0：COM0--->COM63(从上往下扫描)
     * C8：COM63--->COM0(从下往上扫描)
     */
    Oled_Write_Cmd(0xC8);

    /* 双字节命令：设置COM显示偏移量 */
    Oled_Write_Cmd(0xD3);
    Oled_Write_Cmd(0x00); /* COM不偏移 */

    /* 双字节命令：配置COM重映射 */
    Oled_Write_Cmd(0xDA);
    Oled_Write_Cmd(0x12);

    /* 双字节命令：设置预充期 */
    Oled_Write_Cmd(0xD9);
    Oled_Write_Cmd(0x22); /* 阶段一2个无效DCLK时钟/阶段二2个无效DCLK时钟 */

    /* 设置VCOMH取消选择电平
     * 00:0.65xVcc
     * 20:0.77xVcc
     * 30:0.83xVcc
     */
    Oled_Write_Cmd(0xDB);
    Oled_Write_Cmd(0x20);

    /* 双字节命令：设置电荷泵 */
    Oled_Write_Cmd(0x8d);
    Oled_Write_Cmd(0x14);

    Oled_Write_Cmd(0xAF);
}

/**
 * @brief  设置光标
 * @param  x,光标x位置
 *		   y,光标y位置
 * @retval 无
 */
void OLED_SetPos(unsigned char x, unsigned char y)  // 设置起始点坐标
{
    Oled_Write_Cmd(0xb0 + y);
    Oled_Write_Cmd(((x & 0xf0) >> 4) | 0x10);
    Oled_Write_Cmd((x & 0x0f) | 0x01);
}

/**
 * @brief  填充整个屏幕
 * @param  fill_data:要填充的数据
 * @retval 无
 */
void OLED_Fill(unsigned char fill_data)  // 全屏填充
{
    unsigned char m, n;
    for (m = 0; m < 8; m++)
    {
        Oled_Write_Cmd(0xb0 + m);  // page0-page1
        Oled_Write_Cmd(0x00);      // low column start address
        Oled_Write_Cmd(0x10);      // high column start address
        for (n = 0; n < 128; n++)
        {
            Oled_Write_Data(fill_data);
        }
    }
}

/**
 * @brief  清屏
 * @param  无
 * @retval 无
 */
void OLED_CLS(void)  // 清屏
{
    OLED_Fill(0x00);
}

/**
 * @brief  将OLED从休眠中唤醒
 * @param  无
 * @retval 无
 */
void OLED_ON(void)
{
    Oled_Write_Cmd(0X8D);  // 设置电荷泵
    Oled_Write_Cmd(0X14);  // 开启电荷泵
    Oled_Write_Cmd(0XAF);  // OLED唤醒
}

/**
 * @brief  让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
 * @param  无
 * @retval 无
 */
void OLED_OFF(void)
{
    Oled_Write_Cmd(0X8D);  // 设置电荷泵
    Oled_Write_Cmd(0X10);  // 关闭电荷泵
    Oled_Write_Cmd(0XAE);  // OLED休眠
}

/**
 * @brief 显示codetab.h中的ASCII字符,有6*8和8*16可选择
 * @param  x,y : 起始点坐标(x:0~127, y:0~7);
 *					ch[] :- 要显示的字符串;
 *					textsize : 字符大小(1:6*8 ; 2:8*16)
 * @retval 无
 */
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char textsize)
{
    unsigned char c = 0, i = 0, j = 0;
    switch (textsize)
    {
    case 1: {
        while (ch[j] != '\0')
        {
            c = ch[j] - 32;
            if (x > 126)
            {
                x = 0;
                y++;
            }
            OLED_SetPos(x, y);
            for (i = 0; i < 6; i++)
                Oled_Write_Data(F6x8[c][i]);
            x += 6;
            j++;
        }
    }
    break;
    case 2: {
        while (ch[j] != '\0')
        {
            c = ch[j] - 32;
            if (x > 120)
            {
                x = 0;
                y++;
            }
            OLED_SetPos(x, y);
            for (i = 0; i < 8; i++)
                Oled_Write_Data(F8X16[c * 16 + i]);
            OLED_SetPos(x, y + 1);
            for (i = 0; i < 8; i++)
                Oled_Write_Data(F8X16[c * 16 + i + 8]);
            x += 8;
            j++;
        }
    }
    break;
    }
}

/**
 * @brief  OLED_ShowCN，显示codetab.h中的汉字,16*16点阵
 * @param  x,y: 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
 *
 * @retval 无
 */
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char n)
{
    unsigned char wm    = 0;
    unsigned int  adder = 32 * n;
    OLED_SetPos(x, y);
    for (wm = 0; wm < 16; wm++)
    {
        Oled_Write_Data(F16x16[adder]);
        adder += 1;
    }
    OLED_SetPos(x, y + 1);
    for (wm = 0; wm < 16; wm++)
    {
        Oled_Write_Data(F16x16[adder]);
        adder += 1;
    }
}

/**
 * @brief  OLED_DrawBMP，显示BMP位图
 * @param  x0,y0 :起始点坐标(x0:0~127, y0:0~7);x1,y1 : 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
 * @retval 无
 */
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
    unsigned int  j = 0;
    unsigned char x, y;

    if (y1 % 8 == 0)
    {
        y = y1 / 8;
    }
    else
    {
        y = y1 / 8 + 1;
    }
    for (y = y0; y < y1; y++)
    {
        OLED_SetPos(x0, y);
        for (x = x0; x < x1; x++)
        {
            Oled_Write_Data(BMP[j++]);
        }
    }
}
